#ifndef AST_HPP
#define	AST_HPP

#include <cstdlib>
#include <string>
#include <vector>
#include "dotable.hpp"
#include "util.hpp"
#include "ast_visitor.hpp"
using namespace std;
class ASTvisitor;


class declaration {
public:
	declaration(string scalar_variable_name);
	declaration(string array_name, int size);
	bool is_array;
	string name;
	int array_size; // to be ignored if is_array is false
};

class symbol_table {
public:
	symbol_table(vector<declaration*> decls);
	map<string/*name*/, declaration*> declarations;
};


class ASTnode : public dotable {
public:
	ASTnode();
	virtual void acceptVisit(ASTvisitor *visitor) = 0;
	ASTnode *parent;
};


class arith : public ASTnode {
};

class lvalue : public arith {
public:
	string name;
};

class variable : public lvalue {
public:
	variable(string _name);
	void acceptVisit(ASTvisitor *visitor);	
};

class array_element : public lvalue {
public:
	array_element(string _array_name, arith *_index);
	void acceptVisit(ASTvisitor *visitor);	
	arith *index;
};

class constant : public arith {
public:
	constant(int _literal);
	void acceptVisit(ASTvisitor *visitor);	
	int literal;
};

class unary_operator : public arith {
public:
	unary_operator(arith *_arg);
	arith *arg;
};

class anegation : public unary_operator {
public:
	anegation(arith *_arg);
	void acceptVisit(ASTvisitor *visitor);	
};

class binary_operator : public arith {
public:
	binary_operator(arith *_arg1, arith *_arg2);
	arith *arg1;
	arith *arg2;
};

class aplus : public binary_operator {
public:
	aplus(arith *_arg1, arith *_arg2);
	void acceptVisit(ASTvisitor *visitor);	
};

class aminus : public binary_operator {
public:
	aminus(arith *_arg1, arith *_arg2);
	void acceptVisit(ASTvisitor *visitor);	
};

class amultiply : public binary_operator {
public:
	amultiply(arith *_arg1, arith *_arg2);
	void acceptVisit(ASTvisitor *visitor);	
};

class adivide : public binary_operator {
public:
	adivide(arith *_arg1, arith *_arg2);
	void acceptVisit(ASTvisitor *visitor);	
};


class bexp : public ASTnode {
};

class b_true : public bexp {
public:
	void acceptVisit(ASTvisitor *visitor);
};

class b_false : public bexp {
public:
	void acceptVisit(ASTvisitor *visitor);
};

class b_not : public bexp {
public:
	b_not(bexp *_arg);
	void acceptVisit(ASTvisitor *visitor);
	bexp *arg;
};

class b_logical : public bexp {
public:
	b_logical(bexp *_arg1, bexp *_arg2);
	bexp *arg1;
	bexp *arg2;	
};

class b_and : public b_logical {
public:
	b_and(bexp *_arg1, bexp *_arg2);
	void acceptVisit(ASTvisitor *visitor);
};

class b_or : public b_logical {
public:
	b_or(bexp *_arg1, bexp *_arg2);
	void acceptVisit(ASTvisitor *visitor);
};

class b_relational : public bexp {
public:
	b_relational(arith *_arg1, arith *_arg2);
	arith *arg1;
	arith *arg2;	
};

class b_gt : public b_relational {public:b_gt(arith *_arg1, arith *_arg2); void acceptVisit(ASTvisitor *visitor);};
class b_ge : public b_relational {public:b_ge(arith *_arg1, arith *_arg2); void acceptVisit(ASTvisitor *visitor);};
class b_lt : public b_relational {public:b_lt(arith *_arg1, arith *_arg2); void acceptVisit(ASTvisitor *visitor);};
class b_le : public b_relational {public:b_le(arith *_arg1, arith *_arg2); void acceptVisit(ASTvisitor *visitor);};
class b_eq : public b_relational {public:b_eq(arith *_arg1, arith *_arg2); void acceptVisit(ASTvisitor *visitor);};
class b_neq: public b_relational {public:b_neq(arith *_arg1, arith *_arg2); void acceptVisit(ASTvisitor *visitor);};




class statement : public ASTnode {
};

class sequence : public statement {
public:
	sequence(vector<statement*> _statements);
	void acceptVisit(ASTvisitor *visitor);	
	vector<statement*> statements;
};


class atomic_statement : public statement {
public:
};

class mutating_statement : public atomic_statement {
public:
	lvalue *dest;	
};

class assignment : public mutating_statement {
public:
	assignment(lvalue *_lhs, arith *_rhs);
	void acceptVisit(ASTvisitor *visitor);	
	arith *rhs;
};

class s_skip : public atomic_statement {
public:
	s_skip();
	void acceptVisit(ASTvisitor *visitor);	
};

class s_read : public mutating_statement {
public:
	s_read(lvalue *_dest);
	void acceptVisit(ASTvisitor *visitor);	
};

class s_write : public atomic_statement {
public:
	s_write(arith *_arg);
	void acceptVisit(ASTvisitor *visitor);	
	arith *arg;
};

class control_statement : public statement {
public:
	bexp *cond;
};

class s_if : public control_statement {
public:
	s_if(bexp *_cond, statement *_s_true, statement *_s_false);
	void acceptVisit(ASTvisitor *visitor);	
	statement *s_true;
	statement *s_false;
};

class s_while : public control_statement {
public:
	s_while(bexp *_cond, statement *_body);
	void acceptVisit(ASTvisitor *visitor);	
	statement *body;
};


class program : public ASTnode {
public:
	program(statement *_body, symbol_table* _sym_tab);
	void acceptVisit(ASTvisitor *visitor);	
	statement* body;
	symbol_table* sym_tab;
};


std::ostream& operator<<(std::ostream& stream, ASTnode *n);

#endif	/* AST_HPP */

